/*
 * (c) ralfoide, http://gamez.googlecode.com/, 2008
 * Project: gamez
 * License TBD
 */

package util;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;

//-----------------------------------------------

/**
 * Parses a stream with the following format:
 * 
 * RB1\n
 * OBJ:<obj-name>\n
 * Vert <num-vertex>, <x y z>, * num + \n (x y z are floats/doubles)
 * Faces <num-faces>, <v1 v2 v3>, * num + \n (v1..v3 are vertex indexes)
 * OBJ:<next object>\n
 * or end of file.
 */
public class BlenderTextFileReader {

	private static final String WS = " \t\n\f\r,;:";

	private Reader mReader;
	private StringBuffer mTempBuf;
	private boolean mEOF;
	private ArrayList<Object3d> mObjects;

	public static class ParseError extends Exception {
		private static final long serialVersionUID = 1L; // for serialization

		public ParseError(String message) {
			super(message);
		}
	}

	public static class Object3d {
		private String mName;
		private float[] mVertices;
		private float[] mTextureUV;
		private short[] mIndices;

		public Object3d(String name, float[] vertices, float[] textureUV, short[] indices) {
			mName = name;
			mVertices = vertices;
			mTextureUV = textureUV;
			mIndices = indices;
		}
		
		public String getName() {
			return mName;
		}
		public float[] getVertices() {
			return mVertices;
		}
		public float[] getTextureUV() {
			return mTextureUV;
		}
		public short[] getIndices() {
			return mIndices;
		}		
	}
	
	public BlenderTextFileReader(Reader reader) throws ParseError {
		mObjects = new ArrayList<Object3d>();
		mReader = reader;
		mTempBuf = new StringBuffer();
		mEOF = false;

		parse();
		
		// free temporary data
		mTempBuf = null;
		mReader = null;
	}

	public ArrayList<Object3d> getObjects() {
		return mObjects;
	}
	
	private void parse() throws ParseError {
		if (!getToken().equals("RB1")) {
			throw new ParseError("Missing file header");
		}
		
		while (parseObject()) {
			// pass
		}
	}

	private boolean parseObject() throws ParseError {
		skipTo("OBJ");
		if (EOF()) return false;
		
		String objName = getToken();
		if (objName.length() == 0) {
			throw new ParseError("Missing obj name");
		}
		
		float[] vertices = getFloats("Vert", 3);
		float[] textureUV = getFloats("TUV", 2);
		short[] indices = getInts("Faces", 3);
		
		Object3d obj = new Object3d(objName, vertices, textureUV, indices);
		mObjects.add(obj);
		return true;
	}

	private short[] getInts(String name, int numPerPoint) {
		skipTo(name);

		int numFaces = Integer.parseInt(getToken()); 
		short[] indices = new short[numFaces * numPerPoint];
		for (int i = 0, k = 0; i < numFaces; i++) {
			for (int j = 0; j < numPerPoint; j++) {
				indices[k++] = Short.parseShort(getToken());
			}
		}
		return indices;
	}

	private float[] getFloats(String name, int numPerPoint) {
		skipTo(name);
		
		int numVertex = Integer.parseInt(getToken()); 
		float[] vertices = new float[numVertex * numPerPoint];
		for (int i = 0, k = 0; i < numVertex; i++) {
			for (int j = 0; j < numPerPoint; j++) {
				vertices[k++] = Float.parseFloat(getToken());
			}
			
		}
		return vertices;
	}

	private void skipTo(String word) {
		if (!getToken().equals(word)) {
			while (!EOF() && getToken().equals(word)) {
				// pass
			}
		}
	}

	private boolean EOF() {
		return mEOF;
	}
	
	private String getToken() {
		mTempBuf.setLength(0);
		int c = -1;
		try {
			while ((c = mReader.read()) != -1) {
				if (WS.indexOf(c) != -1) {
					if (mTempBuf.length() == 0) {
						continue; // skip initial whitespace
					} else {
						break;
					}
				}
				mTempBuf.append((char)c);
			}
		} catch (IOException e) {
			mEOF = true;
		}
		if (c == -1) {
			mEOF = true;
		}
		
		return mTempBuf.toString();
	}
	
}
